package com.loadagent.core.util;

import javax.crypto.*;
import java.security.interfaces.*;
import java.security.spec.*;
import java.security.*;
import java.math.*;
import javax.crypto.spec.*;
import java.util.*;

public class EncryptUtils {
    public static final char[] SALT;
    private static int KEY_LENGTH;

    public static byte[] en(final byte[] msg, final char[] key, final int type) {
        if (type == 1) {
            return enAES(msg, md5(StrUtils.merger(new char[][]{key, EncryptUtils.SALT}), true));
        }
        return enSimple(msg, key);
    }

    public static byte[] de(final byte[] msg, final char[] key, final int type) {
        if (type == 1) {
            return deAES(msg, md5(StrUtils.merger(new char[][]{key, EncryptUtils.SALT}), true));
        }
        return deSimple(msg, key);
    }

    public static byte[] md5byte(final char[] str) {
        byte[] b = null;
        try {
            final MessageDigest md = MessageDigest.getInstance("MD5");
            final byte[] buffer = StrUtils.toBytes(str);
            md.update(buffer);
            b = md.digest();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return b;
    }

    public static char[] md5(final char[] str) {
        return md5(str, false);
    }

    public static char[] md5(final char[] str, final boolean sh0rt) {
        final byte[] s = md5byte(str);
        if (s == null) {
            return null;
        }
        int begin = 0;
        int end = s.length;
        if (sh0rt) {
            begin = 8;
            end = 16;
        }
        char[] result = new char[0];
        for (int i = begin; i < end; ++i) {
            result = StrUtils.merger(new char[][]{result, Integer.toHexString((0xFF & s[i]) | 0xFFFFFF00).substring(6).toCharArray()});
        }
        return result;
    }

    public static byte[] enSimple(final byte[] msg, final int start, final int end, final char[] key) {
        final byte[] keys = IoUtils.merger(new byte[][]{md5byte(StrUtils.merger(new char[][]{key, EncryptUtils.SALT})), md5byte(StrUtils.merger(new char[][]{EncryptUtils.SALT, key}))});
        for (int i = start; i <= end; ++i) {
            msg[i] ^= keys[i % keys.length];
        }
        return msg;
    }

    public static byte[] deSimple(final byte[] msg, final int start, final int end, final char[] key) {
        final byte[] keys = IoUtils.merger(new byte[][]{md5byte(StrUtils.merger(new char[][]{key, EncryptUtils.SALT})), md5byte(StrUtils.merger(new char[][]{EncryptUtils.SALT, key}))});
        for (int i = start; i <= end; ++i) {
            msg[i] ^= keys[i % keys.length];
        }
        return msg;
    }

    public static byte[] enSimple(final byte[] msg, final char[] key) {
        return enSimple(msg, 0, msg.length - 1, key);
    }

    public static byte[] deSimple(final byte[] msg, final char[] key) {
        return deSimple(msg, 0, msg.length - 1, key);
    }

    public static String enRSA(final String str, final String publicKey) {
        try {
            final byte[] in = str.getBytes("UTF-8");
            final byte[] out = enRSA(in, publicKey);
            final String outStr = Base64.getEncoder().encodeToString(out);
            return outStr;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static byte[] enRSA(final byte[] msg, final String publicKey) {
        try {
            final byte[] decoded = Base64.getDecoder().decode(publicKey.getBytes("UTF-8"));
            final RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
            final Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(1, pubKey);
            return cipherDoFinal(cipher, msg, 1);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static String deRSA(final String str, final String privateKey) {
        try {
            final byte[] inputByte = Base64.getDecoder().decode(str.getBytes("UTF-8"));
            final String outStr = new String(deRSA(inputByte, privateKey));
            return outStr;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static byte[] deRSA(final byte[] msg, final String privateKey) {
        try {
            final byte[] decoded = Base64.getDecoder().decode(privateKey.getBytes("UTF-8"));
            final RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
            final Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(2, priKey);
            return cipherDoFinal(cipher, msg, 2);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private static byte[] cipherDoFinal(final Cipher cipher, final byte[] msg, final int mode) throws Exception {
        int in_length = 0;
        if (mode == 1) {
            in_length = EncryptUtils.KEY_LENGTH / 8 - 11;
        } else if (mode == 2) {
            in_length = EncryptUtils.KEY_LENGTH / 8;
        }
        byte[] in = new byte[in_length];
        byte[] out = new byte[0];
        for (int i = 0; i < msg.length; ++i) {
            if (msg.length - i < in_length && i % in_length == 0) {
                in = new byte[msg.length - i];
            }
            in[i % in_length] = msg[i];
            if (i == msg.length - 1 || i % in_length + 1 == in_length) {
                out = IoUtils.merger(new byte[][]{out, cipher.doFinal(in)});
            }
        }
        return out;
    }

    public static Map<Integer, String> genKeyPair() throws NoSuchAlgorithmException {
        final KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        keyPairGen.initialize(EncryptUtils.KEY_LENGTH, new SecureRandom());
        final KeyPair keyPair = keyPairGen.generateKeyPair();
        final RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        final RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        final BigInteger publicExponent = publicKey.getPublicExponent();
        final BigInteger modulus = publicKey.getModulus();
        final String publicKeyString = new String(Base64.getEncoder().encode(publicKey.getEncoded()));
        final String privateKeyString = new String(Base64.getEncoder().encode(privateKey.getEncoded()));
        final Map<Integer, String> keyMap = new HashMap<Integer, String>();
        keyMap.put(0, publicKeyString);
        keyMap.put(1, privateKeyString);
        keyMap.put(2, modulus.toString(16));
        keyMap.put(3, publicExponent.toString(16));
        return keyMap;
    }

    public static String enAES(final String str, final char[] key) {
        byte[] encrypted = null;
        try {
            encrypted = enAES(str.getBytes("utf-8"), key);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return (encrypted == null) ? null : Base64.getEncoder().encodeToString(encrypted);
    }

    public static byte[] enAES(final byte[] msg, final char[] key) {
        byte[] encrypted = null;
        try {
            final byte[] raw = StrUtils.toBytes(key);
            final SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            final Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(1, skeySpec);
            encrypted = cipher.doFinal(msg);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return encrypted;
    }

    public static String deAES(final String str, final char[] key) {
        String originalString = null;
        final byte[] msg = Base64.getDecoder().decode(str);
        final byte[] original = deAES(msg, key);
        try {
            originalString = new String(original, "utf-8");
        } catch (Exception ex) {
        }
        return originalString;
    }

    public static byte[] deAES(final byte[] msg, final char[] key) {
        byte[] original = null;
        try {
            final byte[] raw = StrUtils.toBytes(key);
            final SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            final Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(2, skeySpec);
            original = cipher.doFinal(msg);
        } catch (Exception ex) {
        }
        return original;
    }

    public static char[] randChar(final int lenght) {
        final char[] result = new char[lenght];
        final Character[] chars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '=', '_', '+', '.'};
        final List<Character> list = Arrays.asList(chars);
        Collections.shuffle(list);
        for (int i = 0; i < lenght; ++i) {
            result[i] = list.get(i);
        }
        return result;
    }

    static {
        SALT = new char[]{'w', 'h', 'o', 'i', 's', 'y', 'o', 'u', 'r', 'd', 'a', 'd', 'd', 'y', '#', '$', '@', '#', '@'};
        EncryptUtils.KEY_LENGTH = 1024;
    }
}
